﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedParameter.Local

/* ParameterUtility.cs -- утилиты для работы с параметрами
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System.Collections.Generic;
using System.Linq;

using AM.Text;

#endregion

#nullable enable

namespace AM.Parameters;

/// <summary>
/// Утилиты для работы с параметрами ИМЯ=ЗНАЧЕНИЕ.
/// </summary>
public static class ParameterUtility
{
    #region Properties

    /// <summary>
    /// Escape character.
    /// </summary>
    public static char EscapeCharacter = '\\';

    /// <summary>
    /// Name separator.
    /// </summary>
    public static char NameSeparator = '=';

    /// <summary>
    /// Value separator.
    /// </summary>
    public static char ValueSeparator = ';';

    #endregion

    #region Public methods

    /// <summary>
    /// Encode parameters to sting representation.
    /// </summary>
    public static string Encode
        (
            Parameter[] parameters
        )
    {
        Sure.NotNull (parameters);

        var builder = StringBuilderPool.Shared.Get();
        char[] badNameCharacters = { NameSeparator };
        char[] badValueCharacters = { ValueSeparator };
        foreach (var parameter in parameters)
        {
            builder.Append
                (
                    Utility.Mangle
                        (
                            parameter.Name,
                            EscapeCharacter,
                            badNameCharacters
                        )
                );
            builder.Append (NameSeparator);
            builder.Append
                (
                    Utility.Mangle
                        (
                            parameter.Value,
                            EscapeCharacter,
                            badValueCharacters
                        )
                );
            builder.Append (ValueSeparator);
        }

        return builder.ReturnShared();
    }

    /// <summary>
    /// Get the parameter with specified name.
    /// </summary>
    public static string? GetParameter
        (
            this Parameter[] parameters,
            string name,
            string? defaultValue
        )
    {
        Sure.NotNull (parameters);
        Sure.NotNullNorEmpty (name);

        var found = parameters
            .FirstOrDefault (p => p.Name.SameString (name));

        var result = ReferenceEquals (found, null)
            ? defaultValue
            : found.Value;

        return result;
    }

    /// <summary>
    /// Get the parameter with specified name.
    /// </summary>
    public static T? GetParameter<T>
        (
            this Parameter[] parameters,
            string name,
            T? defaultValue
        )
    {
        Sure.NotNull (parameters);
        Sure.NotNullNorEmpty (name);

        var found = parameters
            .FirstOrDefault (p => p.Name.SameString (name));

        var result = ReferenceEquals (found, null)
            ? defaultValue
            : Utility.ConvertTo<T> (found.Value);

        return result;
    }

    /// <summary>
    /// Get the parameter with specified name.
    /// </summary>
    public static T? GetParameter<T>
        (
            this Parameter[] parameters,
            string name
        )
    {
        Sure.NotNull (parameters);
        Sure.NotNullNorEmpty (name);

        return GetParameter (parameters, name, default (T));
    }

    /// <summary>
    /// Упрощенный вариант разбора, не заморачивается экранированием символов.
    /// </summary>
    public static Parameter[] SimpleParseString
        (
            string text
        )
    {
        var result = new List<Parameter>();
        var navigator = new TextNavigator (text);
        navigator.SkipWhitespace();

        while (!navigator.IsEOF)
        {
            while (true)
            {
                var flag = false;
                if (navigator.IsWhiteSpace())
                {
                    flag = true;
                    navigator.SkipWhitespace();
                }

                if (navigator.PeekChar() == ValueSeparator)
                {
                    flag = true;
                    navigator.SkipChar (ValueSeparator);
                }

                if (!flag)
                {
                    break;
                }
            }

            var nameMemory = navigator.ReadUntil
                (
                    NameSeparator,
                    ValueSeparator
                );
            if (nameMemory.IsEmpty)
            {
                break;
            }

            var name = nameMemory.ToString().Trim();
            navigator.SkipChar (NameSeparator);
            navigator.SkipWhitespace();

            var value = navigator.ReadUntil (ValueSeparator);
            var parameter = new Parameter (name, value.ToString());
            result.Add (parameter);
        }

        return result.ToArray();
    }

    /// <summary>
    /// Parse specified string.
    /// </summary>
    public static Parameter[] ParseString
        (
            string text
        )
    {
        var result = new List<Parameter>();
        var navigator = new TextNavigator (text);
        navigator.SkipWhitespace();

        while (!navigator.IsEOF)
        {
            while (true)
            {
                var flag = false;
                if (navigator.IsWhiteSpace())
                {
                    flag = true;
                    navigator.SkipWhitespace();
                }

                if (navigator.PeekChar() == ValueSeparator)
                {
                    flag = true;
                    navigator.SkipChar (ValueSeparator);
                }

                if (!flag)
                {
                    break;
                }
            }

            var name = navigator.ReadEscapedUntil
                (
                    EscapeCharacter,
                    NameSeparator
                );
            if (ReferenceEquals (name, null))
            {
                break;
            }

            name = name.Trim();
            navigator.SkipWhitespace();

            var value = navigator.ReadEscapedUntil
                (
                    EscapeCharacter,
                    ValueSeparator
                );
            var parameter = new Parameter (name, value);
            result.Add (parameter);
        }

        return result.ToArray();
    }

    #endregion
}
